{
  "cells": [
    {
      "cell_type": "code",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-01-16T08:19:00.814422Z",
          "start_time": "2025-01-16T08:18:58.434336Z"
        },
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "CYEdC4ltyKO3",
        "outputId": "e8a04cb3-12e4-4fbd-caba-df713be50df7"
      },
      "source": [
        "import matplotlib as mpl\n",
        "import matplotlib.pyplot as plt\n",
        "%matplotlib inline\n",
        "import numpy as np\n",
        "import sklearn\n",
        "import pandas as pd\n",
        "import os\n",
        "import sys\n",
        "import time\n",
        "from tqdm.auto import tqdm\n",
        "import torch\n",
        "import torch.nn as nn\n",
        "import torch.nn.functional as F\n",
        "\n",
        "print(sys.version_info)\n",
        "for module in mpl, np, pd, sklearn, torch:\n",
        "    print(module.__name__, module.__version__)\n",
        "\n",
        "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n",
        "print(device)\n"
      ],
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "sys.version_info(major=3, minor=11, micro=11, releaselevel='final', serial=0)\n",
            "matplotlib 3.10.0\n",
            "numpy 1.26.4\n",
            "pandas 2.2.2\n",
            "sklearn 1.6.1\n",
            "torch 2.5.1+cu124\n",
            "cuda:0\n"
          ]
        }
      ],
      "execution_count": 1
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "yMgh9Yd_yKO5"
      },
      "source": [
        "## 加载数据"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-01-16T08:22:48.713405Z",
          "start_time": "2025-01-16T08:22:48.682256Z"
        },
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "L74b-kwuyKO7",
        "outputId": "84005029-463c-4f5e-e2e8-d5f67509e379"
      },
      "source": [
        "from torchvision import datasets\n",
        "from torchvision.transforms import ToTensor\n",
        "\n",
        "# fashion_mnist图像分类数据集\n",
        "train_ds = datasets.FashionMNIST(\n",
        "    root=\"data\",\n",
        "    train=True,\n",
        "    download=True,\n",
        "    transform=ToTensor()\n",
        ")\n",
        "\n",
        "test_ds = datasets.FashionMNIST(\n",
        "    root=\"data\",\n",
        "    train=False,\n",
        "    download=True,\n",
        "    transform=ToTensor()\n",
        ")\n",
        "\n",
        "# torchvision 数据集里没有提供训练集和验证集的划分\n",
        "# 当然也可以用 torch.utils.data.Dataset 实现人为划分\n",
        "# 从数据集到dataloader\n",
        "train_loader = torch.utils.data.DataLoader(train_ds, batch_size=16, shuffle=True)\n",
        "val_loader = torch.utils.data.DataLoader(test_ds, batch_size=16, shuffle=False)"
      ],
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-images-idx3-ubyte.gz\n",
            "Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-images-idx3-ubyte.gz to data/FashionMNIST/raw/train-images-idx3-ubyte.gz\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "100%|██████████| 26.4M/26.4M [00:01<00:00, 13.4MB/s]\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Extracting data/FashionMNIST/raw/train-images-idx3-ubyte.gz to data/FashionMNIST/raw\n",
            "\n",
            "Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-labels-idx1-ubyte.gz\n",
            "Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/train-labels-idx1-ubyte.gz to data/FashionMNIST/raw/train-labels-idx1-ubyte.gz\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "100%|██████████| 29.5k/29.5k [00:00<00:00, 210kB/s]\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Extracting data/FashionMNIST/raw/train-labels-idx1-ubyte.gz to data/FashionMNIST/raw\n",
            "\n",
            "Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-images-idx3-ubyte.gz\n",
            "Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-images-idx3-ubyte.gz to data/FashionMNIST/raw/t10k-images-idx3-ubyte.gz\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "100%|██████████| 4.42M/4.42M [00:01<00:00, 3.93MB/s]\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Extracting data/FashionMNIST/raw/t10k-images-idx3-ubyte.gz to data/FashionMNIST/raw\n",
            "\n",
            "Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-labels-idx1-ubyte.gz\n",
            "Downloading http://fashion-mnist.s3-website.eu-central-1.amazonaws.com/t10k-labels-idx1-ubyte.gz to data/FashionMNIST/raw/t10k-labels-idx1-ubyte.gz\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "100%|██████████| 5.15k/5.15k [00:00<00:00, 10.7MB/s]"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Extracting data/FashionMNIST/raw/t10k-labels-idx1-ubyte.gz to data/FashionMNIST/raw\n",
            "\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "\n"
          ]
        }
      ],
      "execution_count": 2
    },
    {
      "cell_type": "code",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-01-16T08:23:41.556696Z",
          "start_time": "2025-01-16T08:23:38.810294Z"
        },
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "yfOaxy_6yKO7",
        "outputId": "8d62578e-8428-4446-8922-ce987c89a509"
      },
      "source": [
        "from torchvision.transforms import Normalize\n",
        "\n",
        "# 遍历train_ds得到每张图片，计算每个通道的均值和方差\n",
        "def cal_mean_std(ds):\n",
        "    mean = 0.\n",
        "    std = 0.\n",
        "    for img, _ in ds: # 遍历每张图片,img.shape=[1,28,28]\n",
        "        mean += img.mean(dim=(1, 2))\n",
        "        std += img.std(dim=(1, 2))\n",
        "    mean /= len(ds)\n",
        "    std /= len(ds)\n",
        "    return mean, std\n",
        "\n",
        "\n",
        "print(cal_mean_std(train_ds))\n",
        "# 0.2860， 0.3205\n",
        "transforms = nn.Sequential(\n",
        "    Normalize([0.2860], [0.3205]) # 这里的均值和标准差是通过train_ds计算得到的\n",
        ")\n"
      ],
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "(tensor([0.2860]), tensor([0.3205]))\n"
          ]
        }
      ],
      "execution_count": 3
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "YXGduGbPyKO7"
      },
      "source": [
        "## 定义模型\n",
        "\n",
        "这里我们没有用`nn.Linear`的默认初始化，而是采用了xavier均匀分布去初始化全连接层的权重\n",
        "\n",
        "xavier初始化出自论文 《Understanding the difficulty of training deep feedforward neural networks》，适用于使用`tanh`和`sigmoid`激活函数的方法。当然，我们这里的模型采用的是`relu`激活函数，采用He初始化（何凯明初始化）会更加合适。感兴趣的同学可以自己动手修改并比对效果。\n",
        "\n",
        "|神经网络层数|初始化方式|early stop at epoch| val_loss | vla_acc|\n",
        "|-|-|-|-|-|\n",
        "|20|默认|\n",
        "|20|xaviier_uniform|\n",
        "|20|he_uniform|\n",
        "|...|\n",
        "\n",
        "He初始化出自论文 《Delving deep into rectifiers: Surpassing human-level performance on ImageNet classification》"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "ExecuteTime": {
          "end_time": "2025-01-16T08:24:43.639389Z",
          "start_time": "2025-01-16T08:24:43.631382Z"
        },
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "BsWxS7yTyKO8",
        "outputId": "967866ba-9e5b-41fd-8665-e153d79455a2"
      },
      "source": [
        "class NeuralNetwork(nn.Module):\n",
        "    def __init__(self, layers_num=2):\n",
        "        super().__init__()\n",
        "        self.transforms = transforms # 预处理层，标准化\n",
        "        self.flatten = nn.Flatten()\n",
        "        # 多加几层\n",
        "        self.linear_relu_stack = nn.Sequential(\n",
        "            nn.Linear(28 * 28, 100),\n",
        "            nn.ReLU(),\n",
        "        )\n",
        "        # 加19层\n",
        "        for i in range(1, layers_num):\n",
        "            self.linear_relu_stack.add_module(f\"Linear_{i}\", nn.Linear(100, 100))\n",
        "            self.linear_relu_stack.add_module(f\"relu\", nn.ReLU())\n",
        "        # 输出层\n",
        "        self.linear_relu_stack.add_module(\"Output Layer\", nn.Linear(100, 10))\n",
        "\n",
        "        # 初始化权重\n",
        "        self.init_weights()\n",
        "\n",
        "    def init_weights(self):\n",
        "        \"\"\"使用 xavier 均匀分布来初始化全连接层的权重 W\"\"\"\n",
        "        # print('''初始化权重''')\n",
        "        for m in self.modules():\n",
        "            # print(m)\n",
        "            # print('-'*50)\n",
        "            if isinstance(m, nn.Linear):#判断m是否为全连接层\n",
        "                # https://pytorch.org/docs/stable/nn.init.html\n",
        "                nn.init.xavier_uniform_(m.weight) # xavier 均匀分布初始化权重\n",
        "                nn.init.zeros_(m.bias) # 全零初始化偏置项\n",
        "        # print('''初始化权重完成''')\n",
        "    def forward(self, x):\n",
        "        # x.shape [batch size, 1, 28, 28]\n",
        "        x = self.transforms(x) #标准化\n",
        "        x = self.flatten(x)\n",
        "        # 展平后 x.shape [batch size, 28 * 28]\n",
        "        logits = self.linear_relu_stack(x)\n",
        "        # logits.shape [batch size, 10]\n",
        "        return logits\n",
        "total=0\n",
        "for idx, (key, value) in enumerate(NeuralNetwork(20).named_parameters()):\n",
        "    print(f\"Linear_{idx // 2:>02}\\tparamerters num: {np.prod(value.shape)}\") #np.prod是计算张量的元素个数\n",
        "    print(f\"Linear_{idx // 2:>02}\\tshape: {value.shape}\")\n",
        "    total+=np.prod(value.shape)\n",
        "total"
      ],
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Linear_00\tparamerters num: 78400\n",
            "Linear_00\tshape: torch.Size([100, 784])\n",
            "Linear_00\tparamerters num: 100\n",
            "Linear_00\tshape: torch.Size([100])\n",
            "Linear_01\tparamerters num: 10000\n",
            "Linear_01\tshape: torch.Size([100, 100])\n",
            "Linear_01\tparamerters num: 100\n",
            "Linear_01\tshape: torch.Size([100])\n",
            "Linear_02\tparamerters num: 10000\n",
            "Linear_02\tshape: torch.Size([100, 100])\n",
            "Linear_02\tparamerters num: 100\n",
            "Linear_02\tshape: torch.Size([100])\n",
            "Linear_03\tparamerters num: 10000\n",
            "Linear_03\tshape: torch.Size([100, 100])\n",
            "Linear_03\tparamerters num: 100\n",
            "Linear_03\tshape: torch.Size([100])\n",
            "Linear_04\tparamerters num: 10000\n",
            "Linear_04\tshape: torch.Size([100, 100])\n",
            "Linear_04\tparamerters num: 100\n",
            "Linear_04\tshape: torch.Size([100])\n",
            "Linear_05\tparamerters num: 10000\n",
            "Linear_05\tshape: torch.Size([100, 100])\n",
            "Linear_05\tparamerters num: 100\n",
            "Linear_05\tshape: torch.Size([100])\n",
            "Linear_06\tparamerters num: 10000\n",
            "Linear_06\tshape: torch.Size([100, 100])\n",
            "Linear_06\tparamerters num: 100\n",
            "Linear_06\tshape: torch.Size([100])\n",
            "Linear_07\tparamerters num: 10000\n",
            "Linear_07\tshape: torch.Size([100, 100])\n",
            "Linear_07\tparamerters num: 100\n",
            "Linear_07\tshape: torch.Size([100])\n",
            "Linear_08\tparamerters num: 10000\n",
            "Linear_08\tshape: torch.Size([100, 100])\n",
            "Linear_08\tparamerters num: 100\n",
            "Linear_08\tshape: torch.Size([100])\n",
            "Linear_09\tparamerters num: 10000\n",
            "Linear_09\tshape: torch.Size([100, 100])\n",
            "Linear_09\tparamerters num: 100\n",
            "Linear_09\tshape: torch.Size([100])\n",
            "Linear_10\tparamerters num: 10000\n",
            "Linear_10\tshape: torch.Size([100, 100])\n",
            "Linear_10\tparamerters num: 100\n",
            "Linear_10\tshape: torch.Size([100])\n",
            "Linear_11\tparamerters num: 10000\n",
            "Linear_11\tshape: torch.Size([100, 100])\n",
            "Linear_11\tparamerters num: 100\n",
            "Linear_11\tshape: torch.Size([100])\n",
            "Linear_12\tparamerters num: 10000\n",
            "Linear_12\tshape: torch.Size([100, 100])\n",
            "Linear_12\tparamerters num: 100\n",
            "Linear_12\tshape: torch.Size([100])\n",
            "Linear_13\tparamerters num: 10000\n",
            "Linear_13\tshape: torch.Size([100, 100])\n",
            "Linear_13\tparamerters num: 100\n",
            "Linear_13\tshape: torch.Size([100])\n",
            "Linear_14\tparamerters num: 10000\n",
            "Linear_14\tshape: torch.Size([100, 100])\n",
            "Linear_14\tparamerters num: 100\n",
            "Linear_14\tshape: torch.Size([100])\n",
            "Linear_15\tparamerters num: 10000\n",
            "Linear_15\tshape: torch.Size([100, 100])\n",
            "Linear_15\tparamerters num: 100\n",
            "Linear_15\tshape: torch.Size([100])\n",
            "Linear_16\tparamerters num: 10000\n",
            "Linear_16\tshape: torch.Size([100, 100])\n",
            "Linear_16\tparamerters num: 100\n",
            "Linear_16\tshape: torch.Size([100])\n",
            "Linear_17\tparamerters num: 10000\n",
            "Linear_17\tshape: torch.Size([100, 100])\n",
            "Linear_17\tparamerters num: 100\n",
            "Linear_17\tshape: torch.Size([100])\n",
            "Linear_18\tparamerters num: 10000\n",
            "Linear_18\tshape: torch.Size([100, 100])\n",
            "Linear_18\tparamerters num: 100\n",
            "Linear_18\tshape: torch.Size([100])\n",
            "Linear_19\tparamerters num: 10000\n",
            "Linear_19\tshape: torch.Size([100, 100])\n",
            "Linear_19\tparamerters num: 100\n",
            "Linear_19\tshape: torch.Size([100])\n",
            "Linear_20\tparamerters num: 1000\n",
            "Linear_20\tshape: torch.Size([10, 100])\n",
            "Linear_20\tparamerters num: 10\n",
            "Linear_20\tshape: torch.Size([10])\n"
          ]
        },
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "271410"
            ]
          },
          "metadata": {},
          "execution_count": 4
        }
      ],
      "execution_count": 4
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "tensor([[1., 0., 0., 0., 0.],\n",
              "        [0., 1., 0., 0., 0.],\n",
              "        [0., 0., 1., 0., 0.]])"
            ]
          },
          "metadata": {},
          "execution_count": 5
        }
      ],
      "source": [
        "w = torch.empty(3, 5)\n",
        "nn.init.eye_(w)"
      ],
      "metadata": {
        "ExecuteTime": {
          "end_time": "2024-07-18T08:14:01.335144900Z",
          "start_time": "2024-07-18T08:14:01.295167700Z"
        },
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "wM7BoT7ZyKO8",
        "outputId": "c575f244-7029-4303-ef09-b9101eaaf5a9"
      }
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Gi_ZMcgMyKO8"
      },
      "source": [
        "## 训练"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "metadata": {
        "ExecuteTime": {
          "end_time": "2024-07-18T08:18:23.231667700Z",
          "start_time": "2024-07-18T08:18:22.410532400Z"
        },
        "id": "nohFEMpxyKO8"
      },
      "outputs": [],
      "source": [
        "from sklearn.metrics import accuracy_score\n",
        "\n",
        "@torch.no_grad()\n",
        "def evaluating(model, dataloader, loss_fct):\n",
        "    loss_list = []\n",
        "    pred_list = []\n",
        "    label_list = []\n",
        "    for datas, labels in dataloader:\n",
        "        datas = datas.to(device)\n",
        "        labels = labels.to(device)\n",
        "        # 前向计算\n",
        "        logits = model(datas)\n",
        "        loss = loss_fct(logits, labels)         # 验证集损失\n",
        "        loss_list.append(loss.item())\n",
        "\n",
        "        preds = logits.argmax(axis=-1)    # 验证集预测\n",
        "        pred_list.extend(preds.cpu().numpy().tolist())\n",
        "        label_list.extend(labels.cpu().numpy().tolist())\n",
        "\n",
        "    acc = accuracy_score(label_list, pred_list)\n",
        "    return np.mean(loss_list), acc\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "metadata": {
        "ExecuteTime": {
          "end_time": "2024-07-18T08:18:32.648585100Z",
          "start_time": "2024-07-18T08:18:32.401627500Z"
        },
        "id": "M59OlMkEyKO9"
      },
      "outputs": [],
      "source": [
        "from torch.utils.tensorboard import SummaryWriter\n",
        "\n",
        "\n",
        "class TensorBoardCallback:\n",
        "    def __init__(self, log_dir, flush_secs=10):\n",
        "        \"\"\"\n",
        "        Args:\n",
        "            log_dir (str): dir to write log.\n",
        "            flush_secs (int, optional): write to dsk each flush_secs seconds. Defaults to 10.\n",
        "        \"\"\"\n",
        "        self.writer = SummaryWriter(log_dir=log_dir, flush_secs=flush_secs)\n",
        "\n",
        "    def draw_model(self, model, input_shape):\n",
        "        self.writer.add_graph(model, input_to_model=torch.randn(input_shape))\n",
        "\n",
        "    def add_loss_scalars(self, step, loss, val_loss):\n",
        "        self.writer.add_scalars(\n",
        "            main_tag=\"training/loss\",\n",
        "            tag_scalar_dict={\"loss\": loss, \"val_loss\": val_loss},\n",
        "            global_step=step,\n",
        "            )\n",
        "\n",
        "    def add_acc_scalars(self, step, acc, val_acc):\n",
        "        self.writer.add_scalars(\n",
        "            main_tag=\"training/accuracy\",\n",
        "            tag_scalar_dict={\"accuracy\": acc, \"val_accuracy\": val_acc},\n",
        "            global_step=step,\n",
        "        )\n",
        "\n",
        "    def add_lr_scalars(self, step, learning_rate):\n",
        "        self.writer.add_scalars(\n",
        "            main_tag=\"training/learning_rate\",\n",
        "            tag_scalar_dict={\"learning_rate\": learning_rate},\n",
        "            global_step=step,\n",
        "\n",
        "        )\n",
        "\n",
        "    def __call__(self, step, **kwargs):\n",
        "        # add loss\n",
        "        loss = kwargs.pop(\"loss\", None)\n",
        "        val_loss = kwargs.pop(\"val_loss\", None)\n",
        "        if loss is not None and val_loss is not None:\n",
        "            self.add_loss_scalars(step, loss, val_loss)\n",
        "        # add acc\n",
        "        acc = kwargs.pop(\"acc\", None)\n",
        "        val_acc = kwargs.pop(\"val_acc\", None)\n",
        "        if acc is not None and val_acc is not None:\n",
        "            self.add_acc_scalars(step, acc, val_acc)\n",
        "        # add lr\n",
        "        learning_rate = kwargs.pop(\"lr\", None)\n",
        "        if learning_rate is not None:\n",
        "            self.add_lr_scalars(step, learning_rate)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "metadata": {
        "ExecuteTime": {
          "end_time": "2024-07-18T08:18:38.910670Z",
          "start_time": "2024-07-18T08:18:38.904974600Z"
        },
        "id": "UO8Tw-9HyKO9"
      },
      "outputs": [],
      "source": [
        "class SaveCheckpointsCallback:\n",
        "    def __init__(self, save_dir, save_step=5000, save_best_only=True):\n",
        "        \"\"\"\n",
        "        Save checkpoints each save_epoch epoch.\n",
        "        We save checkpoint by epoch in this implementation.\n",
        "        Usually, training scripts with pytorch evaluating model and save checkpoint by step.\n",
        "\n",
        "        Args:\n",
        "            save_dir (str): dir to save checkpoint\n",
        "            save_epoch (int, optional): the frequency to save checkpoint. Defaults to 1.\n",
        "            save_best_only (bool, optional): If True, only save the best model or save each model at every epoch.\n",
        "        \"\"\"\n",
        "        self.save_dir = save_dir\n",
        "        self.save_step = save_step\n",
        "        self.save_best_only = save_best_only\n",
        "        self.best_metrics = -1\n",
        "\n",
        "        # mkdir\n",
        "        if not os.path.exists(self.save_dir):\n",
        "            os.mkdir(self.save_dir)\n",
        "\n",
        "    def __call__(self, step, state_dict, metric=None):\n",
        "        if step % self.save_step > 0:\n",
        "            return\n",
        "\n",
        "        if self.save_best_only:\n",
        "            assert metric is not None\n",
        "            if metric >= self.best_metrics:\n",
        "                # save checkpoints\n",
        "                torch.save(state_dict, os.path.join(self.save_dir, \"best.ckpt\"))\n",
        "                # update best metrics\n",
        "                self.best_metrics = metric\n",
        "        else:\n",
        "            torch.save(state_dict, os.path.join(self.save_dir, f\"{step}.ckpt\"))\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "metadata": {
        "ExecuteTime": {
          "end_time": "2024-07-18T08:18:43.022006800Z",
          "start_time": "2024-07-18T08:18:43.014428900Z"
        },
        "id": "EfhU5msryKO9"
      },
      "outputs": [],
      "source": [
        "class EarlyStopCallback:\n",
        "    def __init__(self, patience=5, min_delta=0.01):\n",
        "        \"\"\"\n",
        "\n",
        "        Args:\n",
        "            patience (int, optional): Number of epochs with no improvement after which training will be stopped.. Defaults to 5.\n",
        "            min_delta (float, optional): Minimum change in the monitored quantity to qualify as an improvement, i.e. an absolute\n",
        "                change of less than min_delta, will count as no improvement. Defaults to 0.01.\n",
        "        \"\"\"\n",
        "        self.patience = patience\n",
        "        self.min_delta = min_delta\n",
        "        self.best_metric = -1\n",
        "        self.counter = 0\n",
        "\n",
        "    def __call__(self, metric):\n",
        "        if metric >= self.best_metric + self.min_delta:\n",
        "            # update best metric\n",
        "            self.best_metric = metric\n",
        "            # reset counter\n",
        "            self.counter = 0\n",
        "        else:\n",
        "            self.counter += 1\n",
        "\n",
        "    @property\n",
        "    def early_stop(self):\n",
        "        return self.counter >= self.patience\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "metadata": {
        "ExecuteTime": {
          "end_time": "2024-07-18T08:19:20.271204500Z",
          "start_time": "2024-07-18T08:19:20.261309700Z"
        },
        "id": "02az4tlFyKO9"
      },
      "outputs": [],
      "source": [
        "# 训练\n",
        "def training(\n",
        "    model,\n",
        "    train_loader,\n",
        "    val_loader,\n",
        "    epoch,\n",
        "    loss_fct,\n",
        "    optimizer,\n",
        "    tensorboard_callback=None,\n",
        "    save_ckpt_callback=None,\n",
        "    early_stop_callback=None,\n",
        "    eval_step=500,\n",
        "    ):\n",
        "    record_dict = {\n",
        "        \"train\": [],\n",
        "        \"val\": []\n",
        "    }\n",
        "\n",
        "    global_step = 0\n",
        "    model.train()\n",
        "    with tqdm(total=epoch * len(train_loader)) as pbar:\n",
        "        for epoch_id in range(epoch):\n",
        "            # training\n",
        "            for datas, labels in train_loader:\n",
        "                datas = datas.to(device)\n",
        "                labels = labels.to(device)\n",
        "                # 梯度清空\n",
        "                optimizer.zero_grad()\n",
        "                # 模型前向计算\n",
        "                logits = model(datas)\n",
        "                # 计算损失\n",
        "                loss = loss_fct(logits, labels)\n",
        "                # 梯度回传\n",
        "                loss.backward()\n",
        "                # 调整优化器，包括学习率的变动等\n",
        "                optimizer.step()\n",
        "                preds = logits.argmax(axis=-1)\n",
        "\n",
        "                acc = accuracy_score(labels.cpu().numpy(), preds.cpu().numpy())\n",
        "                loss = loss.cpu().item()\n",
        "                # record\n",
        "\n",
        "                record_dict[\"train\"].append({\n",
        "                    \"loss\": loss, \"acc\": acc, \"step\": global_step\n",
        "                })\n",
        "\n",
        "                # evaluating\n",
        "                if global_step % eval_step == 0:\n",
        "                    model.eval()\n",
        "                    val_loss, val_acc = evaluating(model, val_loader, loss_fct)\n",
        "                    record_dict[\"val\"].append({\n",
        "                        \"loss\": val_loss, \"acc\": val_acc, \"step\": global_step\n",
        "                    })\n",
        "                    model.train()\n",
        "\n",
        "                    # 1. 使用 tensorboard 可视化\n",
        "                    if tensorboard_callback is not None:\n",
        "                        tensorboard_callback(\n",
        "                            global_step,\n",
        "                            loss=loss, val_loss=val_loss,\n",
        "                            acc=acc, val_acc=val_acc,\n",
        "                            lr=optimizer.param_groups[0][\"lr\"],\n",
        "                            )\n",
        "\n",
        "                    # 2. 保存模型权重 save model checkpoint\n",
        "                    if save_ckpt_callback is not None:\n",
        "                        save_ckpt_callback(global_step, model.state_dict(), metric=val_acc)\n",
        "\n",
        "                    # 3. 早停 Early Stop\n",
        "                    if early_stop_callback is not None:\n",
        "                        early_stop_callback(val_acc)\n",
        "                        if early_stop_callback.early_stop:\n",
        "                            print(f\"Early stop at epoch {epoch_id} / global_step {global_step}\")\n",
        "                            return record_dict\n",
        "\n",
        "                # udate step\n",
        "                global_step += 1\n",
        "                pbar.update(1)\n",
        "                pbar.set_postfix({\"epoch\": epoch_id})\n",
        "\n",
        "    return record_dict\n",
        "\n",
        "\n",
        "epoch = 100\n",
        "\n",
        "model = NeuralNetwork(layers_num=10)\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "outputs": [],
      "source": [
        "# 1. 定义损失函数 采用交叉熵损失\n",
        "loss_fct = nn.CrossEntropyLoss()\n",
        "# 2. 定义优化器 采用SGD\n",
        "# Optimizers specified in the torch.optim package\n",
        "optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9)\n",
        "\n",
        "# 1. tensorboard 可视化\n",
        "tensorboard_callback = TensorBoardCallback(\"runs\")\n",
        "tensorboard_callback.draw_model(model, [1, 28, 28])\n",
        "# 2. save best\n",
        "save_ckpt_callback = SaveCheckpointsCallback(\"checkpoints\", save_best_only=True)\n",
        "# 3. early stop\n",
        "early_stop_callback = EarlyStopCallback(patience=10, min_delta=0.001)\n",
        "\n",
        "model = model.to(device)"
      ],
      "metadata": {
        "ExecuteTime": {
          "end_time": "2024-07-18T08:19:45.450320900Z",
          "start_time": "2024-07-18T08:19:45.175435500Z"
        },
        "id": "sNOjR74hyKO9"
      }
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "  0%|          | 0/375000 [00:00<?, ?it/s]"
            ],
            "application/vnd.jupyter.widget-view+json": {
              "version_major": 2,
              "version_minor": 0,
              "model_id": "409dc52935b84594a9013a8a6cda9b10"
            }
          },
          "metadata": {}
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Early stop at epoch 31 / global_step 116250\n"
          ]
        }
      ],
      "source": [
        "record = training(\n",
        "    model,\n",
        "    train_loader,\n",
        "    val_loader,\n",
        "    epoch,\n",
        "    loss_fct,\n",
        "    optimizer,\n",
        "    tensorboard_callback=None,\n",
        "    save_ckpt_callback=save_ckpt_callback,\n",
        "    early_stop_callback=early_stop_callback,\n",
        "    eval_step=len(train_loader)\n",
        "    )"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 66,
          "referenced_widgets": [
            "409dc52935b84594a9013a8a6cda9b10",
            "a2b02f4c288c415a9b68c6b15f96f60e",
            "2f421fa4e5d24ee7a8098efd8e1aa839",
            "f862d389aa34459eb1ff553b89ac506a",
            "1ff0c267922847de95901f69d78e1d08",
            "e7eff95d2ae744ee9706a0efcd7a4045",
            "9072bbaabd7647a8aeaead9d27742d4c",
            "c8d1570f581047b5b6ca56ae9fe8c045",
            "977bad61c6f94a7199a483e935b55808",
            "0d1a38035f4b4399aeca222e184854d5",
            "b211f79b10d545d68946bdf77c126197"
          ]
        },
        "id": "NVQpWHPnyKO-",
        "outputId": "b3b88a05-9c56-447c-9c23-ed9e8d05731a"
      }
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "metadata": {
        "ExecuteTime": {
          "end_time": "2023-11-17T13:06:44.722561800Z",
          "start_time": "2023-11-17T13:06:44.373481400Z"
        },
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 465
        },
        "id": "FYpCdOuNyKO-",
        "outputId": "174adee3-ed1a-4427-f09b-5697bc4a8c2a"
      },
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 1200x500 with 2 Axes>"
            ],
            "image/png": "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rlaQLiIiIiIi0sSmg+Xc/vp6dhVVATBrYg53nj+YKJslyCM79YVXn3S3G+oriXSU0ys1BoBctWETEemwnJwcFixY0Krnmkwm3nnnnYCOR0RERNrH6XLzzPLdfP/plewqqqJbfCR///E47pkxTAF6JwmvlfSyXGyPj2KyOYKVIz8GVDxOREREREQE4GBZLXNeX8+Xe0sBmDYsg/mXjSQlNiLIIwsv4RWk22IBsLrs9E6OBCBXvdJFRERERCTMvbfhEP/39kYq6xqIibBwz4yhXHl6NiaTisN1tvBKd4+I9d3sm2T8ZVO6u4gElNsN9urgXNzuVg3xr3/9K927d8flch31+DXXXMMNN9zA7t27ueSSS8jIyCAuLo4zzjiDjz76yG9TtHHjRr773e8SHR1NamoqN998M1VVVb6fL1++nHHjxhEbG0tSUhITJ04kN9coYLNhwwbOO+884uPjSUhIYOzYsaxdu9ZvYxMRETnVVdY5uP21b/j5q99QWdfAqOwkFv38bK46o5cC9CAJs5X0aNyYMOGmlydezy2pxu126y+giASGowYe7B6c9/7toaNOTrbkiiuu4NZbb+WTTz7he9/7HgClpaUsW7aM999/n6qqKqZPn84DDzxAZGQkL730EjNmzGD79u306tWrQ0Osrq5m2rRpjB8/nq+++oqioiJuvPFGZs+ezcKFC2loaODSSy/lpptu4tVXX8Vut7NmzRrf/9nXXnstY8aM4ZlnnsFisbB+/XpsNluHxiQiIhIudlfAn55azcGyOswmmP3dAdz63f7YLOG1lhtqwitIN5kgIgbs1WTFujCboMbu5HBVPenxUcEenYhIUCQnJ3PBBRfwyiuv+IL0N998k9TUVM477zysViujRo3yPf/+++/n7bff5r333mP27Nkdeu9XXnmFuro6XnrpJWJjjRMKTz75JDNmzOCPf/wjNpuN8vJyLrroIvr16wfAkCFDfK/Py8vjV7/6FYMHDwZgwIABHRqPyKlk/5EaVhWamGRvIFEnr0SkCZfLzaMf7eTZzRbc1JGdEs2Cq0YztndKsIcmhFuQDsa+dHs1Ec5auidFc+BILXklNQrSRSQwbDHGinaw3ruVrr32Wm666SaefvppIiMjefXVV7nsssswm81UVVVx7733smjRIvLz82loaKC2tpa8vLwOD3Hr1q2MGjXKF6ADTJw4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\n"
          },
          "metadata": {}
        }
      ],
      "source": [
        "#画线要注意的是损失是不一定在零到1之间的\n",
        "def plot_learning_curves(record_dict, sample_step=500):\n",
        "    # build DataFrame\n",
        "    train_df = pd.DataFrame(record_dict[\"train\"]).set_index(\"step\").iloc[::sample_step]\n",
        "    val_df = pd.DataFrame(record_dict[\"val\"]).set_index(\"step\")\n",
        "\n",
        "    # plot\n",
        "    fig_num = len(train_df.columns)\n",
        "    fig, axs = plt.subplots(1, fig_num, figsize=(6 * fig_num, 5))\n",
        "    for idx, item in enumerate(train_df.columns):\n",
        "        axs[idx].plot(train_df.index, train_df[item], label=f\"train_{item}\")\n",
        "        axs[idx].plot(val_df.index, val_df[item], label=f\"val_{item}\")\n",
        "        axs[idx].grid()\n",
        "        axs[idx].legend()\n",
        "        axs[idx].set_xlabel(\"step\")\n",
        "\n",
        "    plt.show()\n",
        "\n",
        "plot_learning_curves(record, sample_step=10000)  #横坐标是 steps"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "metadata": {
        "ExecuteTime": {
          "end_time": "2024-04-24T03:29:22.432377300Z",
          "start_time": "2024-04-24T03:29:18.976354400Z"
        },
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "ORvYTfX_yKO-",
        "outputId": "09232484-fa62-4135-c741-26e977cb20ce"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stderr",
          "text": [
            "<ipython-input-14-282285e79dd9>:4: FutureWarning: You are using `torch.load` with `weights_only=False` (the current default value), which uses the default pickle module implicitly. It is possible to construct malicious pickle data which will execute arbitrary code during unpickling (See https://github.com/pytorch/pytorch/blob/main/SECURITY.md#untrusted-models for more details). In a future release, the default value for `weights_only` will be flipped to `True`. This limits the functions that could be executed during unpickling. Arbitrary objects will no longer be allowed to be loaded via this mode unless they are explicitly allowlisted by the user via `torch.serialization.add_safe_globals`. We recommend you start setting `weights_only=True` for any use case where you don't have full control of the loaded file. Please open an issue on GitHub for any issues related to this experimental feature.\n",
            "  model.load_state_dict(torch.load(\"checkpoints/best.ckpt\", map_location=\"cpu\"))\n"
          ]
        },
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "loss:     0.3512\n",
            "accuracy: 0.8834\n"
          ]
        }
      ],
      "source": [
        "# dataload for evaluating\n",
        "\n",
        "# load checkpoints\n",
        "model.load_state_dict(torch.load(\"checkpoints/best.ckpt\", map_location=\"cpu\"))\n",
        "\n",
        "model.eval()\n",
        "loss, acc = evaluating(model, val_loader, loss_fct)\n",
        "print(f\"loss:     {loss:.4f}\\naccuracy: {acc:.4f}\")"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.10.8"
    },
    "orig_nbformat": 4,
    "colab": {
      "provenance": [],
      "gpuType": "T4"
    },
    "accelerator": "GPU",
    "widgets": {
      "application/vnd.jupyter.widget-state+json": {
        "409dc52935b84594a9013a8a6cda9b10": {
          "model_module": "@jupyter-widgets/controls",
          "model_name": "HBoxModel",
          "model_module_version": "1.5.0",
          "state": {
            "_dom_classes": [],
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "HBoxModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/controls",
            "_view_module_version": "1.5.0",
            "_view_name": "HBoxView",
            "box_style": "",
            "children": [
              "IPY_MODEL_a2b02f4c288c415a9b68c6b15f96f60e",
              "IPY_MODEL_2f421fa4e5d24ee7a8098efd8e1aa839",
              "IPY_MODEL_f862d389aa34459eb1ff553b89ac506a"
            ],
            "layout": "IPY_MODEL_1ff0c267922847de95901f69d78e1d08"
          }
        },
        "a2b02f4c288c415a9b68c6b15f96f60e": {
          "model_module": "@jupyter-widgets/controls",
          "model_name": "HTMLModel",
          "model_module_version": "1.5.0",
          "state": {
            "_dom_classes": [],
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "HTMLModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/controls",
            "_view_module_version": "1.5.0",
            "_view_name": "HTMLView",
            "description": "",
            "description_tooltip": null,
            "layout": "IPY_MODEL_e7eff95d2ae744ee9706a0efcd7a4045",
            "placeholder": "​",
            "style": "IPY_MODEL_9072bbaabd7647a8aeaead9d27742d4c",
            "value": " 31%"
          }
        },
        "2f421fa4e5d24ee7a8098efd8e1aa839": {
          "model_module": "@jupyter-widgets/controls",
          "model_name": "FloatProgressModel",
          "model_module_version": "1.5.0",
          "state": {
            "_dom_classes": [],
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "FloatProgressModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/controls",
            "_view_module_version": "1.5.0",
            "_view_name": "ProgressView",
            "bar_style": "danger",
            "description": "",
            "description_tooltip": null,
            "layout": "IPY_MODEL_c8d1570f581047b5b6ca56ae9fe8c045",
            "max": 375000,
            "min": 0,
            "orientation": "horizontal",
            "style": "IPY_MODEL_977bad61c6f94a7199a483e935b55808",
            "value": 116250
          }
        },
        "f862d389aa34459eb1ff553b89ac506a": {
          "model_module": "@jupyter-widgets/controls",
          "model_name": "HTMLModel",
          "model_module_version": "1.5.0",
          "state": {
            "_dom_classes": [],
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "HTMLModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/controls",
            "_view_module_version": "1.5.0",
            "_view_name": "HTMLView",
            "description": "",
            "description_tooltip": null,
            "layout": "IPY_MODEL_0d1a38035f4b4399aeca222e184854d5",
            "placeholder": "​",
            "style": "IPY_MODEL_b211f79b10d545d68946bdf77c126197",
            "value": " 116250/375000 [18:48&lt;38:34, 111.79it/s, epoch=30]"
          }
        },
        "1ff0c267922847de95901f69d78e1d08": {
          "model_module": "@jupyter-widgets/base",
          "model_name": "LayoutModel",
          "model_module_version": "1.2.0",
          "state": {
            "_model_module": "@jupyter-widgets/base",
            "_model_module_version": "1.2.0",
            "_model_name": "LayoutModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "LayoutView",
            "align_content": null,
            "align_items": null,
            "align_self": null,
            "border": null,
            "bottom": null,
            "display": null,
            "flex": null,
            "flex_flow": null,
            "grid_area": null,
            "grid_auto_columns": null,
            "grid_auto_flow": null,
            "grid_auto_rows": null,
            "grid_column": null,
            "grid_gap": null,
            "grid_row": null,
            "grid_template_areas": null,
            "grid_template_columns": null,
            "grid_template_rows": null,
            "height": null,
            "justify_content": null,
            "justify_items": null,
            "left": null,
            "margin": null,
            "max_height": null,
            "max_width": null,
            "min_height": null,
            "min_width": null,
            "object_fit": null,
            "object_position": null,
            "order": null,
            "overflow": null,
            "overflow_x": null,
            "overflow_y": null,
            "padding": null,
            "right": null,
            "top": null,
            "visibility": null,
            "width": null
          }
        },
        "e7eff95d2ae744ee9706a0efcd7a4045": {
          "model_module": "@jupyter-widgets/base",
          "model_name": "LayoutModel",
          "model_module_version": "1.2.0",
          "state": {
            "_model_module": "@jupyter-widgets/base",
            "_model_module_version": "1.2.0",
            "_model_name": "LayoutModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "LayoutView",
            "align_content": null,
            "align_items": null,
            "align_self": null,
            "border": null,
            "bottom": null,
            "display": null,
            "flex": null,
            "flex_flow": null,
            "grid_area": null,
            "grid_auto_columns": null,
            "grid_auto_flow": null,
            "grid_auto_rows": null,
            "grid_column": null,
            "grid_gap": null,
            "grid_row": null,
            "grid_template_areas": null,
            "grid_template_columns": null,
            "grid_template_rows": null,
            "height": null,
            "justify_content": null,
            "justify_items": null,
            "left": null,
            "margin": null,
            "max_height": null,
            "max_width": null,
            "min_height": null,
            "min_width": null,
            "object_fit": null,
            "object_position": null,
            "order": null,
            "overflow": null,
            "overflow_x": null,
            "overflow_y": null,
            "padding": null,
            "right": null,
            "top": null,
            "visibility": null,
            "width": null
          }
        },
        "9072bbaabd7647a8aeaead9d27742d4c": {
          "model_module": "@jupyter-widgets/controls",
          "model_name": "DescriptionStyleModel",
          "model_module_version": "1.5.0",
          "state": {
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "DescriptionStyleModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "StyleView",
            "description_width": ""
          }
        },
        "c8d1570f581047b5b6ca56ae9fe8c045": {
          "model_module": "@jupyter-widgets/base",
          "model_name": "LayoutModel",
          "model_module_version": "1.2.0",
          "state": {
            "_model_module": "@jupyter-widgets/base",
            "_model_module_version": "1.2.0",
            "_model_name": "LayoutModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "LayoutView",
            "align_content": null,
            "align_items": null,
            "align_self": null,
            "border": null,
            "bottom": null,
            "display": null,
            "flex": null,
            "flex_flow": null,
            "grid_area": null,
            "grid_auto_columns": null,
            "grid_auto_flow": null,
            "grid_auto_rows": null,
            "grid_column": null,
            "grid_gap": null,
            "grid_row": null,
            "grid_template_areas": null,
            "grid_template_columns": null,
            "grid_template_rows": null,
            "height": null,
            "justify_content": null,
            "justify_items": null,
            "left": null,
            "margin": null,
            "max_height": null,
            "max_width": null,
            "min_height": null,
            "min_width": null,
            "object_fit": null,
            "object_position": null,
            "order": null,
            "overflow": null,
            "overflow_x": null,
            "overflow_y": null,
            "padding": null,
            "right": null,
            "top": null,
            "visibility": null,
            "width": null
          }
        },
        "977bad61c6f94a7199a483e935b55808": {
          "model_module": "@jupyter-widgets/controls",
          "model_name": "ProgressStyleModel",
          "model_module_version": "1.5.0",
          "state": {
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "ProgressStyleModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "StyleView",
            "bar_color": null,
            "description_width": ""
          }
        },
        "0d1a38035f4b4399aeca222e184854d5": {
          "model_module": "@jupyter-widgets/base",
          "model_name": "LayoutModel",
          "model_module_version": "1.2.0",
          "state": {
            "_model_module": "@jupyter-widgets/base",
            "_model_module_version": "1.2.0",
            "_model_name": "LayoutModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "LayoutView",
            "align_content": null,
            "align_items": null,
            "align_self": null,
            "border": null,
            "bottom": null,
            "display": null,
            "flex": null,
            "flex_flow": null,
            "grid_area": null,
            "grid_auto_columns": null,
            "grid_auto_flow": null,
            "grid_auto_rows": null,
            "grid_column": null,
            "grid_gap": null,
            "grid_row": null,
            "grid_template_areas": null,
            "grid_template_columns": null,
            "grid_template_rows": null,
            "height": null,
            "justify_content": null,
            "justify_items": null,
            "left": null,
            "margin": null,
            "max_height": null,
            "max_width": null,
            "min_height": null,
            "min_width": null,
            "object_fit": null,
            "object_position": null,
            "order": null,
            "overflow": null,
            "overflow_x": null,
            "overflow_y": null,
            "padding": null,
            "right": null,
            "top": null,
            "visibility": null,
            "width": null
          }
        },
        "b211f79b10d545d68946bdf77c126197": {
          "model_module": "@jupyter-widgets/controls",
          "model_name": "DescriptionStyleModel",
          "model_module_version": "1.5.0",
          "state": {
            "_model_module": "@jupyter-widgets/controls",
            "_model_module_version": "1.5.0",
            "_model_name": "DescriptionStyleModel",
            "_view_count": null,
            "_view_module": "@jupyter-widgets/base",
            "_view_module_version": "1.2.0",
            "_view_name": "StyleView",
            "description_width": ""
          }
        }
      }
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}